package pl.enigmatic.parser.xml;

import java.util.List;

import org.w3c.dom.Node;

import pl.enigmatic.util.Reflection;
import pl.enigmatic.util.Strings;
import pl.enigmatic.parser.metadata.Metadata;


public class NodeNameInfo {

	public final static String DOT = ".";

	private static String getFullClassName(final List<String> packages, final String nodeName, final boolean prefixed) {
		String clazz = null;
		String sep = DOT;
		if (!nodeName.contains(sep)) {
			if (Strings.upperStarted(nodeName)) {
				clazz = nodeName;
			}
		} else {
			final String[] split = nodeName.split("\\.");
			boolean lower = !prefixed && Strings.lowerStarted(split[0]);
			int start = 1;
			if (lower) {
				clazz = split[0];
			} else {
				clazz = "";
				start = 0;
			}
			final int length = split.length;
			boolean correct = true;
			for (int i = start; i < length && correct; i++) {
				clazz += sep + split[i];
				if (lower = Strings.lowerStarted(split[i])) {
					correct = lower;
				} else {
					sep = "$";
				}
			}
			if (!correct) {
				clazz = null;
			} else if (!Character.isLetter(clazz.charAt(0))) {
				clazz = clazz.substring(1);
			}
		}
		if (clazz != null && Strings.lowerStarted(clazz)) {
			if (Reflection.classExists(clazz)) { return clazz; }
		} else if (packages != null && clazz != null) {
			String res;
			for (final String pkg : packages) {
				res = pkg + DOT + clazz;
				if (Reflection.classExists(res)) { return res; }
			}
		}
		return null;
	}

	public static QualifiedPropertyName getFieldName(final String string, final XMLNSRetriever xmlns) {
		return new QualifiedPropertyName(getFullClassName(Strings.suffixed(string, DOT), xmlns), Strings.suffix(string, DOT));
	}

	private static String getFullClassName(final String prefix, final String nodeName, final XMLNSRetriever xmlns) {
		return getFullClassName(xmlns.get(prefix), nodeName, prefix != null);
	}

	public static String getFullClassName(final String prefixedNodeName, final XMLNSRetriever xmlns) {
		if (prefixedNodeName.contains(":")) {
			final String[] split = prefixedNodeName.split(":");
			return getFullClassName(split[0], split[1], xmlns);
		} else {
			return getFullClassName(null, prefixedNodeName, xmlns);
		}
	}

	public static String getFullClassName(final Node node, final XMLNSRetriever xmlns) {
		String clazz = getFullClassName(node.getPrefix(), node.getLocalName(), xmlns);
		if (clazz == null && Metadata.CLASS.isPresent(node)) {
			clazz = getFullClassName(Metadata.CLASS.getString(node), xmlns);
		}
		return clazz;

	}

	public static class QualifiedPropertyName {

		private final boolean correct;
		private final String clazz;
		private final String property;

		private QualifiedPropertyName(final String clazz, final String property) {
			this.clazz = clazz;
			this.property = property;
			correct = clazz != null && Strings.lowerStarted(property);
		}

		private QualifiedPropertyName(final String property) {
			clazz = null;
			this.property = property;
			correct = Strings.lowerStarted(property);
		}

		/**
		 * @return the qualifying clazz
		 */
		public String getClazz() {
			return clazz;
		}

		/**
		 * @return the property name
		 */
		public String getProperty() {
			return property;
		}

		public boolean isDefaultClass() {
			return clazz == null;
		}

		public boolean isCorrect() {
			return correct;
		}
	}

	private static QualifiedPropertyName getQualifiedPropertyName(final String localName, final String prefix, final XMLNSRetriever xmlns) {
		if (localName.contains(DOT)) {
			final String clazz = NodeNameInfo.getFullClassName(xmlns.get(prefix), Strings.suffixed(localName, DOT), prefix != null);
			return new QualifiedPropertyName(clazz, Strings.suffix(localName, DOT));
		} else {
			return new QualifiedPropertyName(localName);
		}
	}

	public static QualifiedPropertyName getQualifiedPropertyName(final String localName, final XMLNSRetriever xmlns) {
		return getQualifiedPropertyName(localName, null, xmlns);
	}

	public static QualifiedPropertyName getQualifiedPropertyName(final Node node, final XMLNSRetriever xmlns) {
		return getQualifiedPropertyName(node.getLocalName(), node.getPrefix(), xmlns);
	}

	protected final QualifiedPropertyName name;
	/** the parser to use */
	protected final Parser<?> parser;

	public NodeNameInfo(final String property, final Class<?> clazz, final Parser<?> parser) {
		name = new QualifiedPropertyName(clazz.getName(), property);
		this.parser = parser != null ? parser : clazz != null ? Parser.createParser(clazz) : null;
	}

	public NodeNameInfo(final String property, final Class<?> clazz) {
		this(property, clazz, null);
	}

	public NodeNameInfo(final String property, final String clazz, final Parser<?> parser) {
		this(property, Reflection.classForName(clazz), parser);
	}

	public NodeNameInfo(final String property, final String clazz) {
		this(property, Reflection.classForName(clazz));
	}

	public NodeNameInfo(final String property) {
		this(property, (Class<?>) null, (Parser<?>) null);
	}

	public String getProperty() {
		return name.getProperty();
	}

	public Parser<?> getParser() {
		return parser;
	}

}